/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package br.unicamp.ic.sgct.server.recursos.persistencia;

import java.io.BufferedReader;
import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;

/**
 * 
 * @author arthur
 */
public class LoaderDB {

    public static void main(String args[]) {
        String t = "/yuu/.uiu/.";
        System.out.print(t.replaceFirst("(/\\.)$", ""));

    }
    private File logFile;
    private Process process;
    private static LoaderDB loaderDB;
    private static String lib_hsqldb = "/libs/hsqldb/hsqldb-1.8.0.10.jar";
    private static String dir_database = "/database/sgct";

    private LoaderDB() {
    }

    public static LoaderDB getInstance() {
        if (loaderDB == null) {
            loaderDB = new LoaderDB();
        }
        return loaderDB;
    }

    public void startHSQLDB() {
    	try {
            if (!existeHSQLDBExecutando()) {
            	//stopHSQLDB();
            	
                LOGGER.info("INICIANDO SERVIDOR HSQLDB");
                List<String> runCommand = new ArrayList<String>();
                String java_home = System.getenv("JAVA_HOME");
                String java = "java";
                if (java_home != null) {
                    java = java_home + "/bin/java";
                    // java =
                    // "/h2/srv/envsj2ee/programs/java/jdk1.6.0_17/bin/java";
                }
                String path = new File(".").getAbsolutePath();

                if (isWindows()) {
                    java = java.replace("/", "\\");
                    lib_hsqldb = lib_hsqldb.replace("/", "\\");
                    dir_database = dir_database.replace("/", "\\");
                    path = new File(".").getAbsolutePath().replaceFirst("(\\\\.)$", "");
                } else {
                    path = new File(".").getAbsolutePath().replaceFirst("(/\\.)$", "");
                }
                runCommand.add(java);
                runCommand.add("-classpath");
                runCommand.add(path + lib_hsqldb);
                runCommand.add("org.hsqldb.Server");
                runCommand.add("-database");
                runCommand.add(path + dir_database);
                runCommand.add("sgct");
                ProcessBuilder processBuilder = new ProcessBuilder(runCommand);
                // processBuilder.directory(new File(new
                // File().getAbsolutePath()));

                // LOGGER.info("Absolute Path directory command run = " +
                // processBuilder.directory().toString());
                hsqldbLog = new PrintWriter(System.out, true);
                process = processBuilder.start();
                new HSQLDBConsole(process.getInputStream()).start();

                //
                LOGGER.info("JAVA_HOME = " + java_home);
                LOGGER.info("JAVA = " + java);
                LOGGER.info("LIB HSQLDB = " + path + lib_hsqldb);
                LOGGER.info("DIR DATABASE = " + path + dir_database);
                StringBuffer sb = new StringBuffer();
                for (String string : runCommand) {
                    sb.append(string).append(" ");
                }
                LOGGER.info("COMANDO = " + sb.toString());
                int check = 0;
                while(!existeHSQLDBExecutando() && check < 5){
                    Thread.sleep(1000);
                    LOGGER.info("CHECK " + check + " SERVIDOR HSQLDB NAO INICIADO");
                }
                if (existeHSQLDBExecutando()) {
                    LOGGER.info("OK, INICIADO SERVIDOR HSQLDB");
                } else {
                    throw new RuntimeException();
                }
            }
        } catch (Throwable e) {
            LOGGER.info("NAO FOI POSSIVEL INICIAR O SERVIDOR HSQLDB");
            LOGGER.info("POR FAVOR, INICIAR O SERVIDOR ATRAVES DO ANT");
            LOGGER.info("ant bd_start");
            e.printStackTrace();
            if (e instanceof RuntimeException) {
                throw (RuntimeException) e;
            }
        }
    }

    /**
     * Interrompe a execução do servidor. Aguarda o servidor ter sido
     * terminado. O servidor deve estar em execução, caso contrário é
     * lançado um erro.
     *
     * @throws Exception
     *             caso o servidor não esteja em execução ou caso exista
     *             algum problema na configuração que impeça o servidor de
     *             subir.
     */
    public void stopHSQLDB() throws Exception {
        if (process != null) {
            try {
                if (existeHSQLDBExecutando()) {
                    LOGGER.info("ENCERRANDO O SERVIDOR HSQLDB");
                    String processId = getHSQLProcessId();
                    if (processId != null) {
                        killProcess(processId);
                    }
                    LOGGER.info("HSQLDB ENCERRADO");
                } else {
                    LOGGER.info("NAO HA HSQLDB EM EXECUCAO");
                }
            } catch (Throwable e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
                throw new Exception(e);
            }

        } else {
            LOGGER.info("HSQLDB FOI EXECUTADO POR OUTRO VM. NAO ENCERROU A EXECUCAO DO SERVIDOR");
        }
    }

    private static String getHSQLProcessId() throws Exception {
        // usa o programa jps para determinar o processo do HSQLDB
        ProcessBuilder processBuilder = new ProcessBuilder("jps", "-l", "-m");
        Process jpsProcess = processBuilder.start();
        BufferedReader in = new BufferedReader(new InputStreamReader(
                jpsProcess.getInputStream()));
        String line;
        String processId = null;
        while ((line = in.readLine()) != null) {
            if (line.indexOf("org.hsqldb.Server") >= 0) {
                processId = line.substring(0, line.indexOf(' ')).trim();
                break;
            }
        }
        jpsProcess.waitFor();
        return processId;
    }

    /**
     * Encerra o processo com o identificador informado. Utiliza o comando
     * necessário para encerrar o programa de acordo com o sistema operacional.
     *
     * @param processId
     *            identificador do processo a ser eliminado
     * @throws Exception
     *             Caso ocorra algum erro na chamada do comando de encerrar o
     *             processo do sistema operacional.
     */
    private synchronized static void killProcess(String processId) throws Exception {
        Thread.sleep(1000);
        // usa o programa jps para determinar o processo do JBoss
        String[] killCommand;
        if (isWindows()) {
            killCommand = new String[]{"taskkill", "/PID", "/F", processId};
        } else {
            killCommand = new String[]{"kill", processId};
        }

        ProcessBuilder processBuilder = new ProcessBuilder(killCommand);
        Process killProcess = processBuilder.start();
        killProcess.waitFor();
    }

    /**
     * Indica se o ambiente de execução é Windows ou Unix.
     *
     * @return {@code true} caso o ambiente de execução seja Windows ou
     *         {@code false} caso seja Unix.
     */
    private static boolean isWindows() {
        return File.separatorChar == '\\';
    }

    @Override
    protected void finalize() throws Throwable {
        this.stopHSQLDB();
        // fechar base de dados
        super.finalize();
    }

    /**
     * Verifica se existe algum processo de servidor HSQLDB em execução. A
     * busca é limitada aos servidores vinculados ao usuário que executa o
     * programa Java (ele não mostra servidores HSQLDB iniciados por outros
     * usuários).
     *
     * @return {@code true} caso exista pelo menos um servidor HSQLDB em
     *         execução, {@code false} caso contrário
     * @throws Exception
     *             caso ocorra algum erro na execução do comando de
     *             verificação do processo do servior
     */
    private static boolean existeHSQLDBExecutando() throws Throwable {
        boolean hsqldbEncontrado = false;
        // usa o programa jps para determinar o processo do HSQLDB
        ProcessBuilder processBuilder = new ProcessBuilder("jps", "-l", "-m");
        Process jpsProcess = processBuilder.start();
        BufferedReader in = new BufferedReader(new InputStreamReader(
                jpsProcess.getInputStream()));
        String line;
        while ((line = in.readLine()) != null) {
            if (line.indexOf("org.hsqldb.Server") >= 0) {
            	//jpsProcess.waitFor();
                hsqldbEncontrado = true;
                break;
            }
        }
        jpsProcess.waitFor();
        return hsqldbEncontrado;
    }
    private PrintWriter hsqldbLog;

    /**
     * Monitor que copia a saída padrão do servidor para um arquivo de log. Também auxilia
     * na verificação de status do HSQLDB através de análise das mensagens enviadas pelo servidor
     * para o console.
     */
    private class HSQLDBConsole extends Thread {

        private InputStream console;

        public HSQLDBConsole(InputStream console) {
            this.console = console;
            setPriority(MAX_PRIORITY);
            setDaemon(true);
        }

        @Override
        public void run() {
            try {
                BufferedReader in = new BufferedReader(new InputStreamReader(console));
                String line;
                while ((line = in.readLine()) != null) {
                    synchronized (hsqldbLog) {
                        hsqldbLog.println("[HSQLDBConsole] " + line);
                    }
                }
            } catch (Exception e) {
                hsqldbLog.println("=====================  ERRO AO ESCREVER NO ARQUIVO DE LOG =====================");
                e.printStackTrace(hsqldbLog);
                hsqldbLog.println("===============================================================================");
            }
        }
    }
}
